package com.checknull.druineage.customizable;

import com.alibaba.druid.sql.ast.SQLExpr;
import com.alibaba.druid.sql.ast.SQLObject;
import com.alibaba.druid.sql.ast.expr.*;

import java.util.*;

import static com.checknull.druineage.constant.Const.*;

/**
 * 默认字段过滤器（继承重写方法可自定义过滤规则）
 */
public class ColumnFilter {

    public interface ISQLObjectHandle {
        void handle(SQLObject sqlObject, Set<String> usedFor);
    }

    /**
     * 字段血缘作用计算器
     */
    private UsedForCal usedForCal;

    public ColumnFilter(UsedForCal usedForCal) {
        this.usedForCal = usedForCal;
    }

    public UsedForCal getUsedForCal() {
        return usedForCal;
    }

    /**
     * 处理case when中的每个when then
     *
     * @param caseExpr        case when
     * @param sqlObjectHandle 回调
     */
    public void caseFilter(SQLCaseExpr caseExpr, Set<String> usedFor, ISQLObjectHandle sqlObjectHandle) {
        for (SQLCaseExpr.Item item : caseExpr.getItems()) {
            sqlObjectHandle.handle(item, usedForCal.calculate(usedFor, DATA));
        }
        if (caseExpr.getValueExpr() != null) {
            sqlObjectHandle.handle(caseExpr.getValueExpr(), usedForCal.calculate(usedFor, CONDITION));
        }
        sqlObjectHandle.handle(caseExpr.getElseExpr(), usedForCal.calculate(usedFor, DATA));
    }

    /**
     * 分别处理when和then
     *
     * @param caseExprItem    when then
     * @param sqlObjectHandle 回调
     * @description 条件中null不作为血缘
     */
    public void caseItemFilter(SQLCaseExpr.Item caseExprItem, Set<String> usedFor, ISQLObjectHandle sqlObjectHandle) {
        SQLExpr conditionExpr = caseExprItem.getConditionExpr();
        if (conditionExpr instanceof SQLBinaryOpExpr) {
            binaryOpFilter((SQLBinaryOpExpr) conditionExpr, usedFor, sqlObjectHandle);
        } else if (!(conditionExpr instanceof SQLNullExpr) && !(conditionExpr instanceof SQLNotExpr && ((SQLNotExpr) conditionExpr).getExpr() instanceof SQLNullExpr)) {
            sqlObjectHandle.handle(conditionExpr, usedForCal.calculate(usedFor, CONDITION));
        }
        sqlObjectHandle.handle(caseExprItem.getValueExpr(), usedForCal.calculate(usedFor, DATA));
    }

    public void binaryOpFilter(SQLBinaryOpExpr binaryOpExpr, Set<String> usedFor, ISQLObjectHandle sqlObjectHandle) {
        SQLExpr left = binaryOpExpr.getLeft();
        if (left instanceof SQLBinaryOpExpr) {
            binaryOpFilter((SQLBinaryOpExpr) left, usedFor, sqlObjectHandle);
        } else if (!(left instanceof SQLNullExpr)) {
            sqlObjectHandle.handle(left, usedForCal.calculate(usedFor, CONDITION));
        }
        SQLExpr right = binaryOpExpr.getRight();
        if (right instanceof SQLBinaryOpExpr) {
            binaryOpFilter((SQLBinaryOpExpr) right, usedFor, sqlObjectHandle);
        } else if (!(right instanceof SQLNullExpr)) {
            sqlObjectHandle.handle(right, usedForCal.calculate(usedFor, CONDITION));
        }
    }

    public void methodFilter(SQLMethodInvokeExpr methodInvokeExpr, Set<String> usedFor, ISQLObjectHandle sqlObjectHandle) {
        List<SQLExpr> parameters = methodInvokeExpr.getParameters();
        if (methodInvokeExpr.getMethodName().equalsIgnoreCase(IFNULL)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(NVL)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION, DATA));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(NULLIF)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION, DATA));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, CONDITION));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(REGEXP_SUBSTR)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, DATA));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, CONDITION));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(INSTR)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(REGEXP_INSTR)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(CHARINDEX)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(PATINDEX)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, CONDITION));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(ISNULL)) {
            if (parameters.size() > 1) {
                sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION, DATA));
                sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, DATA));
            } else {
                sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION));
            }
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(COALESCE)) {
            for (SQLExpr param : parameters) {
                sqlObjectHandle.handle(param, usedForCal.calculate(usedFor, CONDITION, DATA));
            }
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(IF)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(NVL2)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, CONDITION));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, DATA));
            sqlObjectHandle.handle(parameters.get(2), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(DECODE)) {
            for (int i = 0; i < parameters.size(); i++) {
                if ((i != 0 && i % 2 == 0) || i == parameters.size() - 1) {
                    sqlObjectHandle.handle(parameters.get(i), usedForCal.calculate(usedFor, DATA));
                } else {
                    sqlObjectHandle.handle(parameters.get(i), usedForCal.calculate(usedFor, CONDITION));
                }
            }
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(SUBSTR)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(SUBSTRING)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(TO_NUMBER)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(TO_CHAR)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(TO_DATE)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(LEFT)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(RIGHT)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(STR)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(ROUND)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(TRUNC)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(QUOTENAME)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(REPLICATE)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(CONVERT)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(DATENAME)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(DATEPART)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(STRFTIME)) {
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(REPLACE)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(REGEXP_REPLACE)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, DATA));
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, CONDITION));
            sqlObjectHandle.handle(parameters.get(2), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(STUFF)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, DATA));
            sqlObjectHandle.handle(parameters.get(3), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(LPAD)
                || methodInvokeExpr.getMethodName().equalsIgnoreCase(RPAD)) {
            sqlObjectHandle.handle(parameters.get(0), usedForCal.calculate(usedFor, DATA));
            if (parameters.size() > 2) {
                sqlObjectHandle.handle(parameters.get(2), usedForCal.calculate(usedFor, DATA));
            }
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(DATEADD)) {
            sqlObjectHandle.handle(parameters.get(2), usedForCal.calculate(usedFor, DATA));
        } else if (methodInvokeExpr.getMethodName().equalsIgnoreCase(DATEDIFF)) {
            sqlObjectHandle.handle(parameters.get(1), usedForCal.calculate(usedFor, CONDITION));
            sqlObjectHandle.handle(parameters.get(2), usedForCal.calculate(usedFor, CONDITION));
        } else if (!methodInvokeExpr.getMethodName().equalsIgnoreCase(SPACE)) {
            for (SQLExpr param : parameters) {
                sqlObjectHandle.handle(param, usedForCal.calculate(usedFor, DATA));
            }
        }
    }

}
